<?php

class Adept_Component_ValueControl extends Adept_Component_Control
{
    protected $name = null;

    protected $changed = false;
    protected $localValue;
    protected $valid = true;
    protected $submitted;
    protected $submittedValue;

    protected $required;

    protected $immediate;

    protected $validators = array();
    
    protected $converter;

    const CHANGE_EVENT = 'change';

    public function addValidator($validator)
    {
        $this->validators[] = $validator;
    }

    protected function compareValues($first, $second)
    {
        return $first == $second;
    }

    public function processRequest()
    {
        $request = $this->getContext()->getRequest();
        if ($request[$this->getName()] !== null) {
            $this->submittedValue = $request[$this->getName()];
            $this->submitted = true;

            $this->addValidatePhase();
            $this->addUpdatePhase();
            $this->addEventPhase();

        } else {
            $this->submitted = false;
        }

    }

    /**
     * @throws Adept_Validate_Exception
     */
    protected function validate()
    {
        if ($this->getRequired()) {
            if (strlen(trim($this->submittedValue)) == 0) {
                throw new Adept_Validate_Exception('REQUIRED_FIELD_NEEDED', 0, array('field' => $this->getTitle()));
            }
        }

        if (($validator = $this->getValidator()) != null) {
            $validator->invoke(array($this, $this->submittedValue));
        }
        
        foreach ($this->validators as $validator) {
            $validator->validate($this, $this->submittedValue);
        }        

    }

    public function processValidate()
    {
        try {
            if ($this->submitted) {
                $this->validate();
            }
        } catch (Adept_Validate_Exception $e) {
            $this->valid = false;
            $messageSet = Adept_MessageSet::getInstance();
            $groupId = '';
            if ($form = $this->getParentForm()) {
                $groupId = $form->getId();
                $form->setValid(false);
            }
            $message = new Adept_Message($e->getLocalizedMessage(), Adept_Message::ERROR);
            $messageSet->setMessage($this->getId(), $message, $groupId);
        }
    }
    
    protected function convertToModel($string)
    {
    	if (($converter = $this->getConverter()) != null) {
    	    return $converter->getAsModel($string);
    	}
    	if (($binding = $this->getMethodBinding('converter')) != null) {
    	    return $binding->invoke(array($string, Adept_Converter_IConverter::TO_MODEL));
    	}
    	return $string;
    }
    
    protected function convertToString($model)
    {
    	if (($converter = $this->getConverter()) != null) {
    	    return $converter->getAsString($model);
    	}
    	if (($binding = $this->getMethodBinding('converter')) != null) {
    	    return $binding->invoke(array($model, Adept_Converter_IConverter::TO_STRING));
    	}
    	return $model;    	
    }

    public function processUpdate()
    {
        if (!$this->valid) {
            return ;
        }

        if (!$this->submitted) {
            return ;
        }
        
        if ($form = $this->getParentForm()) {
            if (!$form->isValid()) {
                return ;
            }
        }

        if ($this->getBinding() !== null) {
            $oldValue = $this->getBinding()->getValue();
            if (!$this->compareValues($oldValue, $this->submittedValue)) {
                $this->getBinding()->setValue($this->convertToModel($this->submittedValue));
                $this->changed = true;
            }
        }
    }

    public function processEvent()
    {
        if ($this->changed && $this->valid) {
            $listeners = $this->getListeners(self::CHANGE_EVENT);
            Adept_ClassKit_Delegate_List::invokeChain($listeners, array($this));
        }
    }

    public function getDisplayValue()
    {
        $form = $this->getParentForm();
        if (!$form->isValid()) {
            return $this->submittedValue;
        }
        
        if ($this->getBinding() !== null) {
            return $this->convertToString($this->getBinding()->getValue());
        } else {
            return $this->localValue;
        }
    }

    public function getName()
    {
        if ($this->name) {
            return $this->name;
        } else {
            return $this->getId();
        }
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getParentForm()
    {
        $form = $this->findParentByClass('Adept_Component_Form');
        if ($form == null) {
            throw new Adept_Component_Exception('Cannot found form for ValueControl component');
        }
        return $form;
    }


    /**
     * @return Adept_Binding_IValue
     */
    public function getBinding()
    {
        return $this->getValueBinding('binding');
    }

    /**
     * @param string $binding Binding expression
     */
    public function setBinding($binding)
    {
        $this->setBindingString('binding', $binding);
    }

    public function getConverter() 
    {
        return $this->converter;
    }
    
    public function setConverter($converter) 
    {
        $this->converter = $converter;
    }
    
    /**
     * @return Adept_Binding_IMethod
     */
    public function getValidator()
    {
        return $this->getMethodBinding('validator');

    }

    public function setValidator($validator)
    {
        $this->setBindingString('validator', $validator);
    }

    public function getRequired()
    {
        if (!is_null($this->required)) {
            return $this->required;
        }
        return $this->getValueOfBinding('required', false);
    }

    public function setRequired($required)
    {
        $this->required = $required;
    }

    public function isValid() 
    {
        return $this->valid;
    }
    
    public function setValid($valid) 
    {
        $this->valid = $valid;
    }
    
    public function getValue() 
    {
        if (!is_null($this->localValue)) {
            return $this->localValue;
        }
        return $this->getValueOfBinding('value', null);
    }
    
    public function setValue($value) 
    {
        $this->localValue = $value;
    }

}
